; Performance.s
; Runs on LM3S1968
; Use the SysTick timer to measure approximately how long it takes to
; calculate a square root.  Also, toggle PG2 to use an oscilloscope
; or logic analyzer to measure the execution time as the high pulse
; time of the LED.
; Daniel Valvano
; March 6, 2012

;  This example accompanies the book
;  "Embedded Systems: Introduction to the Arm Cortex M3",
;  ISBN: 978-1469998749, Jonathan Valvano, copyright (c) 2012
;  Program 4.10, Section 4.7
;
;Copyright 2012 by Jonathan W. Valvano, valvano@mail.utexas.edu
;   You may use, edit, run or distribute this file
;   as long as the above copyright notice remains
;THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
;OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
;MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
;VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
;OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
;For more information about my classes, my research, and my books, see
;http://users.ece.utexas.edu/~valvano/

; oscilloscope connected to PG2

        IMPORT   PLL_Init
        IMPORT   SysTick_Init

GPIO_PORTG2        EQU 0x40026010
GPIO_PORTG_DIR_R   EQU 0x40026400
GPIO_PORTG_AFSEL_R EQU 0x40026420
GPIO_PORTG_DEN_R   EQU 0x4002651C
SYSCTL_RCGC2_R     EQU 0x400FE108
SYSCTL_RCGC2_GPIOG EQU 0x00000040   ; port G Clock Gating Control
NVIC_ST_CURRENT_R  EQU 0xE000E018

        AREA    |.text|, CODE, READONLY, ALIGN=2
        THUMB
        EXPORT  Start
Start
    BL  PLL_Init                    ; set system clock to 50 MHz
    BL  SysTick_Init                ; initialize SysTick timer, see SysTick.s
;start of SysTick test section (comment this section for LED test)
;    MOV R0, #0                      ; R0 = 0 (0^2)
;    MOV R0, #3                      ; R0 = 3 (1.73^2)
    MOV R0, #100                    ; R0 = 100 (10^2)
;    MOV R0, #230400                 ; R0 = 230,400 (480^2)
    LDR R4, =NVIC_ST_CURRENT_R      ; R4 = &NVIC_ST_CURRENT_R
    LDR R5, [R4]                    ; R5 = [R4] = initialTime
    BL  sqrt                        ; R0 = sqrt(R0)
;    BL  sqrt2                       ; R0 = sqrt2(R0)
    LDR R6, [R4]                    ; R6 = [R4] = finalTime
    SUB R7, R5, R6                  ; R7 = R5 - R6 = elapsedTime
    AND R7, R7, #0x00FFFFFF         ; R7 = R7&0x00FFFFFF (handle case where timer rolls over)
    B   .                           ; stall here and observe the registers
    ; R0 = result of square root function
    ; R1 = loop counter when function finished (meaningful for sqrt2; always 0 for sqrt)
    ; R2 = intermediate value (not really meaningful, same as R0)
    ; R3 = intermediate value (not meaningful for sqrt; for sqrt2 if different than R2, function did not converge, which is common when input is n^2-1, where n=integer)
    ; R4 = pointer to SysTick Current Register (not really meaningful, always 0xE000E018)
    ; R5 = initialTime SysTick timer value when function started
    ; R6 = finalTime SysTick timer value when function finished
    ; R7 = elapsedTime number of cycles required to execute sqrt function
;end of SysTick test section
;start of PG2 LED test section
    ; activate clock for Port G
    LDR R1, =SYSCTL_RCGC2_R         ; R1 = &SYSCTL_RCGC2_R
    LDR R0, [R1]                    ; R0 = [R1]
    ORR R0, R0, #SYSCTL_RCGC2_GPIOG ; R0 = R0|SYSCTL_RCGC2_GPIOG
    STR R0, [R1]                    ; [R1] = R0
    NOP
    NOP                             ; allow time to finish activating
    ; set direction register
    LDR R1, =GPIO_PORTG_DIR_R       ; R1 = &GPIO_PORTG_DIR_R
    LDR R0, [R1]                    ; R0 = [R1]
    ORR R0, R0, #0x04               ; R0 = R0|0x04 (make PG2 output)
    STR R0, [R1]                    ; [R1] = R0
    ; regular port function
    LDR R1, =GPIO_PORTG_AFSEL_R     ; R1 = &PIO_PORTG_AFSEL_R
    LDR R0, [R1]                    ; R0 = [R1]
    BIC R0, R0, #0x04               ; R0 = R0&~0x04 (disable alt funct on PG2) (default setting)
    STR R0, [R1]                    ; [R1] = R0
    ; enable digital port
    LDR R1, =GPIO_PORTG_DEN_R       ; R1 = &GPIO_PORTG_DEN_R
    LDR R0, [R1]                    ; R0 = [R1]
    ORR R0, R0, #0x04               ; R0 = R0|0x04 (enable digital I/O on PG2) (default setting on LM3S811, not default on other microcontrollers)
    STR R0, [R1]                    ; [R1] = R0
    LDR R4, =GPIO_PORTG2            ; R4 = &GPIO_PORTG2
    MOV R5, #0x04                   ; R5 = 0x04 (turn on LED)
    MOV R6, #0x00                   ; R6 = 0x00 (turn off LED)
loop
    STR R5, [R4]                    ; [R4] = R5 (turn on LED)
;    MOV R0, #0                      ; R0 = 0 (0^2)
;    MOV R0, #3                      ; R0 = 3 (1.73^2)
    MOV R0, #100                    ; R0 = 100 (10^2)
;    MOV R0, #230400                 ; R0 = 230,400 (480^2)
    BL  sqrt                        ; R0 = sqrt(R0)
;    BL  sqrt2                       ; R0 = sqrt2(R0)
    STR R6, [R4]                    ; [R4] = R6 (turn off LED)
    ; anything can go here, after the LED goes off before the branch
    ; use the oscilloscope/logic analyzer to measure high pulse time
    ; this can be easier to see if there is also some low pulse time
;    MOV R0, #0                      ; R0 = 0 (0^2)
;    MOV R0, #3                      ; R0 = 3 (1.73^2)
    MOV R0, #100                    ; R0 = 100 (10^2)
;    MOV R0, #230400                 ; R0 = 230,400 (480^2)
    BL  sqrt                        ; R0 = sqrt(R0)
;    BL  sqrt2                       ; R0 = sqrt2(R0)
    B   loop                        ; repeat
;end of PG2 LED test section

;------------sqrt------------
; Use Newton's method to take the square root of an integer.
; The best and worst number of cycles per instruction is given.
; The total number of cycles varies from 120 to 328.  328
; cycles is approximately 6.56 usec at 50 MHz.  The SysTick
; timer indicates that it takes about 201 cycles to take the
; square root of 100 or 220 cycles to take the square root
; of 230,400 (480^2).
; Input: R0  an integer
; Output: R0 contains square root of input integer
; Modifies: R0, R1, R2, R3
sqrt                                ; (cycles)
    LSR R2, R0, #4                  ; (1) R2 = R0>>4 = R0/16 = previousGuess
    ADD R2, R2, #1                  ; (1) R2 = R2 + 1 = R0/16 + 1
    MOV R1, #16                     ; (1) R1 = 16 = loopIndex
sqrt_loop
    MUL R3, R2, R2                  ; (1) R3 = R2*R2 = currentGuess
    ADD R3, R3, R0                  ; (1) R3 = R3 + R0 = R2*R2 + R0
    UDIV R2, R3, R2                 ; (2-12) R2 = R3/R2 = (R2*R2 + R0)/R2
    LSR R2, R2, #1                  ; (1) R2 = R2>>1 = R2/2
    SUBS R1, R1, #1                 ; (1) R1 = R1 - 1
    BNE sqrt_loop                   ; (1-4) if (loopIndex != 0), continue looping
    MOV R0, R2                      ; (1) R0 = R2
    BX  LR                          ; (4) return

;------------sqrt2------------
; Use Newton's method to take the square root of an integer.
; This time, the loop breaks early when the answer converges.
; The best and worst number of cycles per instruction is given.
; The total number of cycles varies from 168 to 424 if the loop
; runs 16 times and does not break early.  424 cycles is
; approximately 8.48 usec at 50 MHz.  The SysTick timer
; indicates that it takes about 39 cycles to take the square
; root of 100 or 146 cycles to take the square root of 230,400
; (480^2).
; Input: R0  an integer
; Output: R0 contains square root of input integer
; Modifies: R0, R1, R2, R3
sqrt2                               ; (cycles)
    LSR R2, R0, #4                  ; (1) R2 = R0>>4 = R0/16 = previousGuess
    ADD R2, R2, #1                  ; (1) R2 = R2 + 1 = R0/16 + 1
    MOV R1, #16                     ; (1) R1 = 16 = loopIndex
sqrt2_loop
    MUL R3, R2, R2                  ; (1) R3 = R2*R2 = currentGuess
    ADD R3, R3, R0                  ; (1) R3 = R3 + R0 = R2*R2 + R0
    UDIV R3, R3, R2                 ; (2-12) R3 = R3/R2 = (R2*R2 + R0)/R2
    LSR R3, R3, #1                  ; (1) R3 = R3>>1 = R3/2
    CMP R2, R3                      ; (1) is (currentGuess == previousGuess)?
    BEQ sqrt2_done                  ; (1-4) if so, done
    MOV R2, R3                      ; (1) otherwise, previousGuess = currentGuess
    SUBS R1, R1, #1                 ; (1) R1 = R1 - 1
    BNE sqrt2_loop                  ; (1-4) if (loopIndex != 0), continue looping
sqrt2_done
    MOV R0, R2                      ; (1) R0 = R2
    BX  LR                          ; (4) return

    ALIGN                           ; make sure the end of this section is aligned
    END                             ; end of file
